Repository navigation
Conversation
Wires the signature address scanning feature into the confirmation flow using `extractSignatureAddresses` from `@metamask/phishing-controller` rather than a local copy of the extractor. After PPOM validates a v3/v4 typed-data signature request and returns Benign or Errored, address-typed fields in the EIP-712 message are extracted and scanned against the security-alerts API. Results are surfaced as alerts via the new `useSignatureAddressAlerts` hook, naming the specific field (recipient, token, maker, etc.) in each alert. Note: requires a bump to the `@metamask/phishing-controller` version that exports `extractSignatureAddresses` (pending MetaMask/core PR). Related: MetaMask#45058
|
CLA Signature Action: All authors have signed the CLA. You may need to manually re-run the blocking PR check if it doesn't pass in a few minutes. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Reviewed by Cursor Bugbot for commit e9638eb. Configure here.
| ).catch((error) => { | ||
| console.error('Error scanning signature address', error); | ||
| }); | ||
| } |
There was a problem hiding this comment.
Is it ensure that app state is also cleared, else can this alert show for next valid signature also ?
There was a problem hiding this comment.
No, the app state is not explicitly cleared between requests. Cached results expire after 15
minutes via TTL. The alert won't show on the next signature unless that request also contains the
same flagged address, since the hook only reads cache entries for addresses present in the current
confirmation's typed data.
…12 typed-data scanning (MetaMask#10170) ## Explanation Ticket: [PSAFE-441](https://consensyssoftware.atlassian.net/browse/PSAFE-441) Supersedes [MetaMask#9875](MetaMask#9875) (same change, opened from a core branch so changelog CI can resolve the head). Review comments on that PR were addressed there. MetaMask validates `eth_signTypedData_v3` and `eth_signTypedData_v4` requests with PPOM, which inspects the address fields of a signature. PPOM's threat data is refreshed on a delay, so an address that has been flagged recently can still be reported as benign, and it does not cover every chain or protocol. Separately, the real-time per-address scan (`/address/evm/scan`) is only applied to a few fields today: the token `verifyingContract`, permit `spender`, and delegation `delegate`. Addresses carried in any other field — recipients, makers, tokens, custom protocol fields — receive no real-time scan at all. This change adds a schema-driven extractor that returns up to 10 distinct `address`-typed values found in a typed-data message (including nested structs and arrays, matched by declared type rather than field name), and reports when the message could not be fully walked because the address cap, depth limit, or work budget was reached. When PPOM has not already flagged the request, the extracted addresses are passed to the real-time address scan. Results reuse the existing address cache, so fields already scanned elsewhere are not requested again. ### What this exports - `extractSignatureAddresses(typedData, options)` — walks `types`/`primaryType`/`message`, returns `{ addresses, fields, overflow, maxAddresses }` - `ExtractedSignatureAddresses` — return type - `ExtractSignatureAddressesOptions` — options: `exclude` (addresses to skip, e.g. the signer), `excludeFields` (top-level field names to skip, e.g. `spender` when already covered by an existing scan), `maxAddresses` (optional cap override; default 10, hard ceiling 50) - `DEFAULT_MAX_SIGNATURE_ADDRESSES` and `MAX_SIGNATURE_ADDRESSES_CEILING` — exported so clients can word overflow copy and stay aligned with the default/ceiling ### Safeguards - Cap of 10 distinct addresses per message by default, overridable via `maxAddresses` up to 50 - Max traversal depth of 12 - Max 5000 nodes visited - `overflow: true` is returned whenever the walk is incomplete so callers can surface a caution ## References - Prior fork PR (same change): MetaMask#9875 - MetaMask Extension (client wiring): MetaMask/metamask-extension#45521 - MetaMask Mobile (client wiring): MetaMask/metamask-mobile#34786 - Prior implementation with local extractor copy (Extension): MetaMask/metamask-extension#45058 - Prior implementation with local extractor copy (Mobile): MetaMask/metamask-mobile#34428 ## Changelog See `packages/phishing-controller/CHANGELOG.md`. ## Checklist - [x] Tests written and passing - [x] Changelog updated - [x] Exports added to `index.ts` - [ ] Version bump (pending release cut) [PSAFE-441]: https://consensyssoftware.atlassian.net/browse/PSAFE-441?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Security-adjacent signing flow support: incorrect normalization or missed addresses could weaken typed-data scanning when clients adopt the export, though behavior is heavily tested and bounded with overflow signaling. > > **Overview** > Adds **`extractSignatureAddresses`**, a schema-driven helper that walks EIP-712 `types`/`primaryType`/`message` and collects distinct **`address`-typed** values (including nested structs and arrays) for downstream real-time address scanning—not hard-coded field names like `spender` or `to`. > > The function **normalizes** values the same way signing does (hex/decimal, leading 20 bytes, lowercase dedupe), supports **`exclude`**, top-level **`excludeFields`**, and a configurable distinct-address cap (**default 10**, ceiling **50**). It returns **`addresses`**, per-address **`fields`** for alert copy, and **`overflow`** when traversal hits caps, depth (12), or a 5000-node budget so clients can warn when not every address was collected. **`DEFAULT_MAX_SIGNATURE_ADDRESSES`** and **`MAX_SIGNATURE_ADDRESSES_CEILING`** are exported from **`index.ts`** alongside the new types. > > Ships **`signature-address-extraction.ts`**, a large Jest suite (including **`@metamask/eth-sig-util`** parity checks), changelog entry, and **`eth-sig-util`** as a **devDependency** only—no **`PhishingController`** wiring in this PR. > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit 732e1c3. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Alex Donesky <adonesky@gmail.com>

Summary
Ticket: PSAFE-441
MetaMask validates
eth_signTypedData_v3andeth_signTypedData_v4requests with PPOM, which inspects the address fields of a signature. PPOM's threat data is refreshed on a delay, so an address that has been flagged recently can still be reported as benign, and it does not cover every chain or protocol.Separately, the real-time per-address scan (
/address/evm/scan) is only applied to a few fields today: the tokenverifyingContract, permitspender, and delegationdelegate. Addresses carried in any other field — recipients, makers, tokens, custom protocol fields — receive no real-time scan at all.This change wires a schema-driven extractor (added to
@metamask/phishing-controllerin MetaMask/core#9875) that returns up to 10 distinctaddress-typed values found in a typed-data message, including nested structs and arrays, matched by declared type rather than field name. When PPOM has not already flagged the request, those addresses are passed to the real-time address scan. Results reuse the existing address cache, so fields already scanned elsewhere are not requested again. A confirmation alert surfaces any flagged address using the existing alert template, naming the flagged field and address. If some addresses could not be checked because the cap, depth limit, or work budget was reached, a separate caution is shown.Changes
app/scripts/lib/trust-signals/scan-unvalidated-signature.ts(new) — extracts up to 10 address-typed fields and scans each against the real-time API after PPOM; excludes the signer, zero address, and permitspenderwhenverifyingContractis already scannedui/pages/confirmations/hooks/alerts/useSignatureAddressAlerts.ts(new) — React hook that reads scan results and returns alerts for malicious/warning addresses (naming the field) plus a caution when the walk was incompleteapp/scripts/lib/ppom/ppom-middleware.ts— callsscanUnvalidatedSignatureAddressesafter PPOM resolves for signing methods when PPOM has not flagged the requestapp/scripts/lib/ppom/ppom-util.ts— updatesvalidateRequestWithPPOMreturn type toPromise<SecurityAlertResponse>in all pathsui/pages/confirmations/hooks/useConfirmationAlerts.ts— wiresuseSignatureAddressAlertsinto the confirmation alert pipelinealertMessageSignatureAddressMalicious,alertMessageSignatureAddressWarning,alertMessageSignatureAddressScanIncomplete,alertReasonSignatureAddressScanIncompleteDependencies
Blocked on MetaMask/core#9875. The
package.jsondependency on@metamask/phishing-controllermust be bumped to the version that exportsextractSignatureAddressesbefore this can merge.References
Test plan
fromfield (e.g.recipient,token,maker) — confirm a Danger alert appears naming the field and shortened addressspenderalert comes fromuseSpenderAlerts, not duplicated hereScreenshots/Recordings
Signing the same typed-data request on a build without this change and a build with it. The request carries a flagged address in a non-permit field.
Before (no scan of arbitrary address fields — the flagged address is not surfaced):
Address.Scanning.Before.mov
After (the flagged address is scanned and the standard warning is shown):
Address.Scanning.After.mov